import { Injectable, UnauthorizedException } from '@nestjs/common';
import { User } from '@prisma/client';
import { UserDto, UserPaginationDto } from './interfaces/user.dto';
import { PrismaService } from 'src/common/prisma/prisma.service';
import { ERROR_USERNAME_DUPLICATE } from 'src/common/types/error-mapping';
import { ErrorResponse } from 'src/common/types/common.type';
import { CreateUserDto } from 'src/dtos/uses/users.dto';
import { JwtService } from '@nestjs/jwt';
import { UserEntity } from 'src/dtos/serialization/user.dto';
import { plainToInstance } from 'class-transformer';

@Injectable()
export class UsersService {
    constructor(private prisma: PrismaService, private jwtService: JwtService) { }

    async findAllSelectFields(): Promise<UserDto[]> {
        return await this.prisma.user.findMany({
            select: {
                id: true,
                username: true,
                email: true,
                createdAt: true,
                updatedAt: true,
                posts: true,
                profile: true,
            },
            // where: {
            //     posts: {
            //         some: {
            //             content: {
            //                 not: null,
            //             },
            //         },
            //     },
            // },
        });
    }

    async findAllWithConvert(): Promise<UserDto[]> {
        const users = await this.prisma.user.findMany({
            include: {
                posts: true,
                profile: true,
            },
        });
        return users.map(user => ({
            id: user.id,
            username: user.username,
            email: user.email,
            createdAt: user.createdAt,
            updatedAt: user.updatedAt,
            posts: user.posts,
            profile: user.profile,
        }));
    }

    /**
     * return this.prisma.user.findMany({
        include: {
            posts: {
            where: { content: { not: null } }, // 只加载有内容的 post
            take: 10, // 最多加载 10 条
            orderBy: { createdAt: 'desc' }, // 按创建时间倒序
            },
        },
        });
     */

    async createUser(userDto: CreateUserDto): Promise<User | ErrorResponse> {
        // check username duplicate
        if (await this.findUserByUsername(userDto.username)) {
            return ERROR_USERNAME_DUPLICATE
        }

        return await this.prisma.user.create({
            data: {
                username: userDto.username,
                email: userDto.email,
                password: userDto?.password || '',
                updatedAt: new Date(),
            },
        });
    }

    async findUserByUsername(username: string): Promise<User | null> {
        return await this.prisma.user.findFirst({
            where: {
                username,
            },
        });
    }
    async getUserCount(): Promise<number> {
        return await this.prisma.user.count();
    }

    async searchUsers(query: string): Promise<UserDto[]> {
        return await this.prisma.user.findMany({
            where: {
                OR: [{ username: { contains: query } }, { email: { contains: query } }],
            },
            include: { posts: true, profile: true, },
        });
    }

    async findUserWithPagination(pageNum = 1, pageSize = 10): Promise<UserPaginationDto> {
        // 防止出现负数
        pageNum = Math.max(1, pageNum);
        pageSize = Math.max(1, pageSize);
        const skip = (pageNum - 1) * pageSize;
        const [total, users] = await this.prisma.$transaction([
            this.prisma.user.count(),
            this.prisma.user.findMany({
                skip,
                take: pageSize,
                include: { posts: true, profile: true, },
                orderBy: { id: 'desc', }
            }),
        ]);
        return { total, pageNum, pageSize, users, };
    }

    async findUserById(id: number): Promise<UserEntity | null> {
        const user = await this.prisma.user.findFirst({
            where: {
                id,
            },
        });
        return user ? plainToInstance(UserEntity, user) : null;
    }
}
